package hr.fer.zpr.nmbp.project3.dao;

import hr.fer.zpr.nmbp.project3.dao.exceptions.DAOException;
import hr.fer.zpr.nmbp.project3.dao.util.ObjectFromXMLGen;
import hr.fer.zpr.nmbp.project3.dao.util.XMLGen;
import hr.fer.zpr.nmbp.project3.model.Bolest;
import hr.fer.zpr.nmbp.project3.model.Dijagnoza;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.exist.xmldb.XQueryService;
import org.w3c.dom.Node;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.ResourceIterator;
import org.xmldb.api.base.ResourceSet;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.XMLResource;

public class DijagnozaDAO {
	
	XQueryService xqs;

	public DijagnozaDAO(Collection col) {
		try {
			this.xqs = (XQueryService)col.getService("XQueryService", "1.0");
		} catch (XMLDBException e) {
			throw new DAOException("Greška u radu s bazom");
		}
	}

	public void save(Dijagnoza dijagnoza) {
		if(dijagnoza.getKarton() == null){
			throw new DAOException("Dijagnoza nema pripadajući karton");
		}
		if(dijagnoza.getKarton().getPacijent() == null){
			throw new DAOException("Dijagnoza nema pripadajućeg pacijenta");
		}
		
		// Update dijagnoze ako postoji u bazi
    	try {
			if(this.exists(dijagnoza)) {
				this.update(dijagnoza);
				return;
			}
    	
	    	// Generiraj novu sifru ako ju nema
	    	if(dijagnoza.getSifra() == 0){
	    		dijagnoza.setSifra(this.generateNextSifra());
	    	}
	    	
	    	// Spremanje nove dijagnoze u bazu
			int pacijentMbo = dijagnoza.getKarton().getPacijent().getMbo();
			xqs.query("update insert " + XMLGen.getXML(dijagnoza) + "preceding /ebolnica//pacijent" +
					"[@mbo=" + pacijentMbo + "]/karton/kilaza");
			this.saveReferences(dijagnoza);
		} catch (XMLDBException e) {
			throw new DAOException("Greška u radu s bazom!");
		} 
	}

	private int generateNextSifra() {
    	int nextSifra;
    	try {
    		ResourceSet resSet = xqs.query("let $res := /ebolnica//dijagnoza/@sifra return <nextid>{ " +
					"if (count($res) > 0) then max($res)+1 else 0+1 }</nextid>");
			Node node = (Node)((XMLResource)resSet.getIterator().nextResource()).getContentAsDOM();
			nextSifra = Integer.parseInt(node.getNodeValue());
		} catch (XMLDBException e) {
			throw new DAOException("Greška u radu s bazom!");
		}
    	
    	return nextSifra;
	}

	private void update(Dijagnoza dijagnoza) {
		// Ažuriranje dijagnoze
    	try {
			xqs.query("update replace /ebolnica//dijagnoza[@sifra=" + dijagnoza.getSifra() + "] " +
					"with " + XMLGen.getXML(dijagnoza));
			this.saveReferences(dijagnoza);
		} catch (XMLDBException e) {
			throw new DAOException("Greška u radu s bazom!");
		}
	}

	private boolean exists(Dijagnoza dijagnoza) throws XMLDBException {
		if(dijagnoza.getKarton() == null){
			return false;
		}
		if(dijagnoza.getKarton().getPacijent() == null){
			return false;
		}
		
    	ResourceSet set = xqs.query("/ebolnica//dijagnoza[@sifra="+dijagnoza.getSifra()+"]");
    	if(set.getSize() > 0){
    		return true;
    	}else{
    		return false;
    	}
	}

	public void delete(Dijagnoza dijagnoza) {
		// Brisanje dijagnoze
    	try {
			xqs.query("update delete /ebolnica/dijagnozaBolestVeze/dijagnozaBolest" +
					"[@dijagnoza=" + dijagnoza.getSifra() + "]");
			xqs.query("update delete /ebolnica//dijagnoza[@sifra=" + dijagnoza.getSifra() + "]");
		} catch (XMLDBException e) {
			throw new DAOException("Greška u radu s bazom!");
		}
	}

    /**
     * Gleda sifru i karton.
     * @param dijagnozaPrototype
     * @return 
     */
	public List<Dijagnoza> load(Dijagnoza dijagnozaPrototype) {
		List<Dijagnoza> allDijagnoze = this.loadAll();
    	List<Dijagnoza> filteredDijagnoze = new ArrayList<Dijagnoza>();
    	
    	
    	for(Dijagnoza dijagnoza : allDijagnoze){
    		
    		if (dijagnoza.getSifra() == dijagnozaPrototype.getSifra())
    				filteredDijagnoze.add(dijagnoza);
    				
    		if (dijagnoza.getKarton() != null && dijagnoza.getKarton().getPacijent() != null){
    			if ((dijagnoza.getKarton().getPacijent().getMbo() == 
    							dijagnozaPrototype.getKarton().getPacijent().getMbo())) {
    				filteredDijagnoze.add(dijagnoza);
    			}
    		}
    		
    	}
    	
    	return filteredDijagnoze;
	}

	public List<Dijagnoza> loadAll() {
		ArrayList<Dijagnoza> loadedDijagnoze = new ArrayList<Dijagnoza>();
    	
    	// Učitavanje dijagnoza
    	ResourceSet resSet = null;
    	try {
			resSet = xqs.query("/ebolnica//dijagnoza");

    		ResourceIterator iter = resSet.getIterator();
			while(iter.hasMoreResources()){
				XMLResource res = (XMLResource) iter.nextResource();
				Dijagnoza tempDijagnoza = (Dijagnoza)ObjectFromXMLGen.createInstance(res.getContentAsDOM());
				if(tempDijagnoza != null){
					this.loadReferences(tempDijagnoza);
					loadedDijagnoze.add(tempDijagnoza);
				}
			}
		} catch (XMLDBException e) {
			throw new DAOException("Greška u radu s bazom!");
		}
    	
    	return loadedDijagnoze;
	}

	public List<Dijagnoza> load(final Date dateFrom, final Date dateTo) {
		List<Dijagnoza> allDijagnoze = this.loadAll();
		List<Dijagnoza> filteredDijagnoze = new ArrayList<Dijagnoza>();
		
		if(dateFrom == null && dateTo == null){
			return allDijagnoze;
		}
		
		for(Dijagnoza dijagnoza : allDijagnoze){
			boolean accept = true;
			if(dijagnoza.getDatum() != null){
				
				if(dateFrom != null && dijagnoza.getDatum().compareTo(dateFrom) < 0){
					accept = false;
				}
				
				if(dateTo != null && dijagnoza.getDatum().compareTo(dateTo) > 0){
					accept = false;
				}
				
				if(accept){
					filteredDijagnoze.add(dijagnoza);
				}
			}
		}
		
		return filteredDijagnoze;
	}
	
	public List<Dijagnoza> load(final List<Bolest> bolesti){
		List<Dijagnoza> allDijagnoze = this.loadAll();
		List<Dijagnoza> filteredDijagnoze = new ArrayList<Dijagnoza>();
		
		for(Dijagnoza dijagnoza : allDijagnoze){
			if(dijagnoza.getBolesti() != null){
				if(dijagnoza.getBolesti().containsAll(bolesti)){
					filteredDijagnoze.add(dijagnoza);
				}
			}
		}
		
		return filteredDijagnoze;
	}
	
	private void saveReferences(Dijagnoza dijagnoza){
		try {
			// Brisanje
			xqs.query("update delete /ebolnica/dijagnozaBolestVeze/dijagnozaBolest" +
					"[@dijagnoza=" + dijagnoza.getSifra() + "]");
			
			// Dodavanje
			List<String> veze = XMLGen.getXMLBolestDijagnoza(dijagnoza);
			for(String veza : veze){
				xqs.query("update insert " + veza + " into /ebolnica/dijagnozaBolestVeze");
			}
		} catch (XMLDBException e) {
			throw new DAOException("Greška u radu s bazom!");
		}
	}
	
	private void loadReferences(Dijagnoza dijagnoza){
		try {
			ResourceSet resSet = xqs.query("for $v in /ebolnica//dijagnozaBolest[@dijagnoza=" + 
					dijagnoza.getSifra() + "] return /ebolnica/bolesti/bolest[@sifra=data($v/@bolest)]");
			
			ResourceIterator iter = resSet.getIterator();
			List<Bolest> refBolesti = new ArrayList<Bolest>(); 
			while(iter.hasMoreResources()){
				XMLResource res = (XMLResource) iter.nextResource();
				Bolest tempBolest = (Bolest)ObjectFromXMLGen.createInstance(res.getContentAsDOM());
				if(tempBolest != null){
					refBolesti.add(tempBolest);
				}
			}
			dijagnoza.setBolesti(refBolesti);
		} catch (XMLDBException e) {
			throw new DAOException("Greška u radu s bazom!");
		}
	}
}
 